<?php defined('SYSPATH') or die('No direct script access.');
/**
 * GeoIP Service
 *
 * $Id$
 *
 * @package    Core
 * @author     Sam Clark
 * @copyright  (c) 2008 Polaris Digital
 * @license    GNU Public Licence v3
 */
class Geoip_Core {

	// Configuration
	protected $config = array(
		'key'		=> '',
		'driver'	=> 'maxmind'
	);

	/**
	 * Return a Geoip object
	 * @return
	 * @param $config Object[optional]
	 */
	public static function factory( $config = array() )
	{
		return new Geoip( $config );
	}

	/**
	 * Return a static instance of Geoip.
	 *
	 * @return  object
	 */
	public static function instance( $config = array() )
	{
		static $instance;

		empty( $instance ) and $instance = new Geoip( $config );

		return $instance;
	}

	/**
	 * Construct this library and load the correct driver
	 * @return
	 */
	public function __construct( $config = array() )
	{
		// Setup config
		$config += Kohana::config('geoip');

		$this->config = $config;

		// Set driver name
		$driver = 'Geoip_'.ucfirst($this->config['driver']).'_Driver';

		// Load the driver
		if ( ! Kohana::auto_load($driver) )
		{
			throw new Kohana_Exception( 'geoip.driver_not_supported', $this->config['driver'] );
		}

		// Initialize the driver
		$this->driver = new $driver($this->config);

		// Validate the driver
		if ( ! ($this->driver instanceof Geoip_Driver) )
		{
			throw new Kohana_Exception('geoip.driver_not_supported', 'Geoip drivers must use the Geoip_Interface.');
		}
	}

	/**
	 * Queries the supplied IP
	 * @return either Geoip Result object or FALSE
	 * @param $ip Object
	 */
	public function query($ip)
	{
		// Setup the result
		$result = FALSE;

		// If this isn't a private ip address
		if ($this->driver->validate_ip($ip))
		{
			// If there is no cache available
			if ( ! $result = $this->retrieve_cache($ip))
			{
				// Do the query
				$result = $this->driver->query($ip);
				// Cache the result
				$this->cache_result($result, $ip);
			}
		}

		// Return the result
		return $result;
	}

	/**
	 * Returns the fulltext region name if country_code and region_code are supplied
	 * @return
	 * @param $country_code Object[optional]
	 * @param $region_code Object[optional]
	 * @param $plain_text Object[optional] If true, only the name will be returned
	 */
	public static function get_region_name( $country_code = FALSE, $region_code = FALSE, $plain_text = FALSE )
	{
		$result = FALSE;

		if( self::validate_region_code( $country_code, $region_code ) )
		{
			$region = ORM::factory('international_region')
								->where( array( 'country_code' => $country_code, 'region_code' => $region_code ) )
								->limit(1)
								->find_all();

			$result = $plain_text ? $region->current()->region_name : $region->current();
		}

		return $result;
	}


	/**
	 * Validates whether a country_code is valid
	 * @return (bool) TRUE or FALSE
	 * @param $country_code Object[optional]
	 */
	public static function validate_country_code( $country_code = FALSE )
	{
		if( $country_code )
		{
			return (bool) Database::instance()
								->select('country_code')
								->where( array( 'country_code' => $country_code ) )
								->limit( 1 )
								->get( 'international_regions' )
								->count();
		}
		else
		{
			return FALSE;
		}
	}

	/**
	 * Validates whether a country_code and region_code combination are valid
	 * @return (bool) TRUE or FALSE
	 * @param $country_code Object[optional]
	 * @param $region_code Object[optional]
	 */
	public static function validate_region_code( $country_code = FALSE, $region_code = FALSE )
	{
		if( $country_code and $region_code )
		{
			return (bool) Database::instance()
								->select('region_code')
								->where( array( 'country_code' => $country_code, 'region_code' => $region_code ) )
								->limit( 1 )
								->get( 'international_regions' )
								->count();
		}
		else
		{
			return FALSE;
		}
	}


	/**
	 * Caches the resulting data from a GEO_IP request
	 * @return (bool) TRUE or FALSE
	 * @param $geoip_data Object[optional]
	 * @param $ip Object[optional]
	 */
	protected function cache_result( $geoip_data = NULL, $ip = FALSE )
	{
		// Setup the result
		$result = FALSE;

		// If geoip_data is present and an IP address is present
		if( $geoip_data instanceof Geoip_Result and $ip )
		{
			// Create new cache
			$cache = Cache::instance();

			// Create cache name
			$cache_name = "geoip_{$ip}";

			// Create the cache
			$cache->set( $cache_name, $geoip_data, array( 'geoip', $cache_name ), 86400 );

			// Set the result
			$result = TRUE;
		}

		// Return the result
		return $result;
	}

	/**
	 * Retrieves geo_ip cache data if available
	 * @return Geoip_Result or FALSE
	 * @param $ip Object[optional]
	 */
	protected function retrieve_cache( $ip = FALSE )
	{
		// Setup result
		$result = FALSE;

		// If there is an ip address supplied
		if( $ip )
		{
			// Load a cache instance
			$cache = Cache::instance();

			// Setup the cache name
			$cache_name = "geoip_{$ip}";

			// Search for the cache name
			$geoip = $cache->get( $cache_name );

			// If there is a valid result
			if( $geoip != NULL )
			{
				// Set the result to be returned
				$result = $geoip;
			}
		}
		// Return the result
		return $result;
	}
}
